import { View, Map, Feature } from 'ol'
import { Projection, addProjection, useGeographic, get, transformExtent } from 'ol/proj'
import { defaults as controlDefaults } from 'ol/control/defaults'
import { defaults as interactionDefaults } from 'ol/interaction/defaults'
import {
  InitMapOptions,
  PictureMarkerLayerOptions,
  PointLayerOptions,
  XYZLayerOptions,
  TextMarkerLayerOptions,
  LineLayerOptions,
  PolygonLayerOptions,
  GeojsonLayerOptions,
  HeatMapLayerOptions,
  VectorTileLayerOptions,
  WMTSLayerOptions,
  ResolutionsAndMids,
  GoToLayerOptions,
  CenterAndZoomOptions,
  goToFeatureOptions,
  FlyLineOptions,
  checkLongitudeAndLatitudeOptions
} from '../types/olutil'

import ImageStyle from 'ol/style/Image'
import CircleStyle from 'ol/style/Circle'

import { Style, Fill, Text, Stroke, Icon } from 'ol/style'

import { LineString, MultiPolygon, Point, Polygon } from 'ol/geom'
import VectorSource from 'ol/source/Vector'
import { Cluster, VectorTile, XYZ } from 'ol/source'

import VectorLayer from 'ol/layer/Vector'
import VectorImage from 'ol/layer/VectorImage'
import TileLayer from 'ol/layer/Tile'
import BaseLayer from 'ol/layer/Base'
import Heatmap from 'ol/layer/Heatmap'
import VectorTileLayer from 'ol/layer/VectorTile'


import GeoJSON from 'ol/format/GeoJSON'
import MVT from 'ol/format/MVT'

import WMTSTileGrid from 'ol/tilegrid/WMTS'
import * as olExtent from 'ol/extent'
import WMTS from 'ol/source/WMTS'
import { FlyLine } from './flyLine'
/**
 * openlayer工具类
 */
class OlUtil {
  map: Map | undefined
  constructor() {
    this.map = undefined
  }

  /**
   * 实例化ol/Map对象
   * @param mapContainer  HTMLElement | string | undefined 渲染地图dom的id
   * @param {InitMapOptions} options  可选配置项
   * @returns 实例化ol/Map对象
   */
  initMap(mapContainer: HTMLElement | string | undefined, options: InitMapOptions): Map {
    useGeographic() // api使用4326坐标系
    this.map = new Map({
      target: mapContainer,
      view: new View({
        projection: options.projection || 'EPSG:3857',
        center: options.center,
        zoom: options.zoom || 8,
        minZoom: options.minZoom || 6,
        maxZoom: options.maxZoom || 18,
        constrainResolution: options.constrainResolution || true, // 设置该参数为true来让每次缩放结束后自动缩放到距离最近的一个整数级别
        enableRotation: options.enableRotation || false // 设置是否允许旋转
      }),
      layers: options.layers,
      controls: controlDefaults({ zoom: false, rotate: false }), // 隐藏缩放及旋转按钮
      interactions: interactionDefaults({doubleClickZoom: options.doubleClickZoom || false}) // 禁用双击缩放
    })
    return this.map
  }

  /**
   * 获取指定id的图层
   * @param id string 图层id
   * @returns OlBaseLayer | undefined 图层对象
   */
  getLayer(id: string): BaseLayer | TileLayer | VectorLayer | VectorImage | Heatmap | VectorTileLayer | undefined {
    if (!this.map) return
    return this.map
      .getLayers()
      .getArray()
      .find(function (layer) {
        const layerId = layer.get('id')
        return layerId && layerId === id
      })
  }

  /**
   * 删除指定id的图层
   * @param id string 图层id
   */
  removeLayer(id: string) {
    if (!this.map) return
    const delLayer = this.map
      .getLayers()
      .getArray()
      .find(function (layer) {
        const layerId = layer.get('id')
        return layerId && layerId === id
      })
    delLayer && this.map.removeLayer(delLayer)
  }

  /**
   * 设置图层显示隐藏
   * @param id string 图层id
   * @param visible boolean 是否显示
   */
  setLayerVisible(id: string, visible: boolean) {
    const layer = this.getLayer(id)
    layer && layer.setVisible(visible)
  }

  /**
   * 设置图层透明度
   * @param id string 图层id
   * @param opacity number 0-1 透明度
   */
  // 设置图层透明度 id:string 图层id opacity:number 0-1 透明度
  setLayerOpacity(id: string, opacity: number) {
    const layer = this.getLayer(id)
    layer && layer.setOpacity(opacity)
  }

  /**
   * 创建点类型的图层
   * @param id string 图层id
   * @param dataArr Array<any> 对象数组，包含点位及相关信息
   * @param {PointLayerOptions} options  配置项
   * @returns VectorLayer | undefined
   */
  createPointsLayer(id: string, dataArr: Array<any>, options?: PointLayerOptions): VectorLayer | undefined {
    const map = this.map
    if (!map) return
    options = options || {}
    if (options.visible === undefined) {
      options.visible = true
    }
    let style = new Style({
      image: new CircleStyle({
        radius: 10,
        fill: new Fill({
          color: '#f00'
        })
      })
    })
    style = options.style || style

    // 设置聚合的圆点的放大样式
    const clusterScaleStyle = style.clone()
    let clusterRadius = (style.getImage() as CircleStyle).getRadius() * 1.5
    if (clusterRadius < 10) {
      // 防止聚合点位过小
      clusterRadius = 15
    }
    ;(clusterScaleStyle?.getImage() as CircleStyle).setRadius(clusterRadius)

    if (dataArr && dataArr.length > 0) {
      let source: Cluster | VectorSource
      const vectorSource = new VectorSource()
      let vectorLayer: VectorLayer | undefined = undefined
      dataArr.forEach((res, index) => {
        const x = Number(res.longitude) || Number(res.x)
        const y = Number(res.latitude) || Number(res.y)
        let feature = new Feature({
          geometry: new Point([x, y]),
          attributes: res,
          c_name: 'point_' + index // 保证唯一性，在聚合显示时用于区分独立的 style
        })
        if (res.style) {
          feature.setStyle(res.style)
        }
        vectorSource.addFeature(feature)
      })
      if (options.cluster) {
        const clusterSource = new Cluster({
          distance: options.distance || 20,
          source: vectorSource
        })
        source = clusterSource
        const styleCache = {} as any
        vectorLayer = new VectorLayer({
          source: source,
          visible: options.visible,
          style: function (feature) {
            const size = feature.get('features').length
            let _style
            if (size === 1) {
              // 当前的标注仅包含一个feature时，采用该feature的样式显示，而不是统一聚合样式。
              _style = styleCache[feature.get('features')[0].get('c_name')]
            } else {
              _style = styleCache[size]
            }
            if (!_style) {
              _style = (options && options.clusterStyle) || [
                new Style({
                  image: clusterScaleStyle.getImage() as ImageStyle | undefined,
                  text: new Text({
                    text: size.toString(),
                    scale: 1.2,
                    stroke: new Stroke({
                      color: 'white',
                      width: 2
                    }),
                    fill: new Fill({
                      color: 'black'
                    })
                  })
                })
              ]
              if (size === 1) {
                // 这里采用那么作为styleCache对象key的值，c_name是唯一的，
                // 这样就可以为为每一个feature设置不同的样式，因为起初feature未聚合前，
                // 可能会用使用不同的图标来表示不同的目标
                const pkiaa = feature.get('features')[0].get('c_name')
                _style = feature.get('features')[0].getStyle()
                styleCache[pkiaa] = _style
              } else {
                styleCache[size] = _style
              }
            }
            return _style
          }
        })
      } else {
        source = vectorSource
        vectorLayer = new VectorLayer({
          source: source,
          visible: options.visible
        })
        vectorLayer.setStyle(style)
      }
      vectorLayer.set('id', id) // 给图层赋予id
      map && map.addLayer(vectorLayer)
      return vectorLayer
    } else {
      console.log('传入数据错误!图层创建失败')
    }
  }

  /**
   * 创建图片标记符号的图层
   * @param id string 图层id
   * @param dataArr Array<any> 对象数组，包含点位及相关信息
   * @param { PictureMarkerLayerOptions } options  配置项
   * @returns VectorImage | undefined
   */
  createPictureMarkerLayer(
    id: string,
    dataArr: Array<any>,
    options?: PictureMarkerLayerOptions
  ): VectorImage | undefined {
    const map = this.map
    if (!map) return

    options = options || {}
    if (options.visible === undefined) {
      options.visible = true
    }
    let style = new Style({
      image: new Icon({
        anchor: [0.5, 0.5], // 锚点
        opacity: 1,
        width: 64,
        height: 64,
        src: 'map/img/default.png'
      })
    })
    style = options.style || style
    if (dataArr && dataArr.length > 0) {
      let source
      let vectorLayer: VectorImage | undefined = undefined
      const vectorSource = new VectorSource()
      dataArr.forEach((res, index) => {
        const x = Number(res.longitude) || Number(res.x)
        const y = Number(res.latitude) || Number(res.y)
        let feature = new Feature({
          geometry: new Point([x, y]),
          attributes: res,
          c_name: 'point_' + index // 保证唯一性，在聚合显示时用于区分独立的 style
        })
        if (res.style) {
          feature.setStyle(res.style)
        }
        vectorSource.addFeature(feature)
      })
      if (options.cluster) {
        const clusterSource = new Cluster({
          distance: options.distance || 80,
          source: vectorSource
        })
        source = clusterSource
        const styleCache = {} as any
        vectorLayer = new VectorImage({
          source: source,
          visible: options.visible,
          style: function (feature) {
            const size = feature.get('features').length
            let _style
            if (size === 1) {
              // 当前的标注仅包含一个feature时，采用该feature的样式显示，而不是统一聚合样式。
              _style = styleCache[feature.get('features')[0].get('c_name')]
            } else {
              _style = styleCache[size]
            }
            if (!_style) {
              _style = (options && options.clusterStyle) || [
                new Style({
                  image: style.clone().getImage() as ImageStyle | undefined,
                  text: new Text({
                    text: size.toString(),
                    scale: 1.3,
                    stroke: new Stroke({
                      color: 'white',
                      width: 1
                    }),
                    fill: new Fill({
                      color: 'black'
                    }),
                    offsetY: (options && options.textOffsetY) || 10
                  })
                })
              ]
              if (size === 1) {
                // 这里采用那么作为styleCache对象key的值，c_name是唯一的，
                // 这样就可以为为每一个feature设置不同的样式，因为起初feature未聚合前，
                // 可能会用使用不同的图标来表示不同的目标
                const pkiaa = feature.get('features')[0].get('c_name')
                _style = feature.get('features')[0].getStyle()
                styleCache[pkiaa] = _style
              } else {
                styleCache[size] = _style
              }
            }
            return _style
          }
        })
      } else {
        source = vectorSource
        vectorLayer = new VectorImage({
          source: source,
          visible: options.visible
        })
        vectorLayer.setStyle(style)
      }
      vectorLayer.set('id', id)
      map.addLayer(vectorLayer)
      return vectorLayer
    } else {
      console.log('传入数据错误!')
    }
  }

  /**
   * 创建文字标记符号图层
   * @param id string 图层id
   * @param dataArr Array<any> 对象数组，包含点位及相关信息
   * @param {TextMarkerLayerOptions} options  配置项
   * @returns VectorLayer | undefined
   */
  // (showTextKey:string 需要显示的字段名称 style:obj 点的样式 visible:boolean 图层是否显示)
  createTextMarkerLayer(id: string, dataArr: Array<any>, options?: TextMarkerLayerOptions): VectorLayer | undefined {
    const map = this.map
    if (!map) return

    options = options || {}
    options.showTextKey = options.showTextKey || 'name'
    if (options.visible === undefined) {
      options.visible = true
    }
    const createItemStyle = (showText: string) => {
      return new Style({
        text: new Text({
          textAlign: 'center', // 位置
          textBaseline: 'middle', // 基准线
          // font: 'normal 16px 微软雅黑', // 文字样式
          scale: 1.8,
          text: showText, // 文本内容
          stroke: new Stroke({
            color: 'white',
            width: 2
          }),
          fill: new Fill({
            color: 'black'
          })
        })
      })
    }
    if (dataArr && dataArr.length > 0) {
      const source = new VectorSource()
      dataArr.forEach((res) => {
        const x = Number(res.longitude) || Number(res.x)
        const y = Number(res.latitude) || Number(res.y)
        let feature = new Feature({
          geometry: new Point([x, y]),
          attributes: res
        })
        if (res.style) {
          feature.setStyle(res.style)
        } else {
          feature.setStyle(
            options!.style ||
              (options!.showTextKey && res[options!.showTextKey] && createItemStyle(res[options!.showTextKey]))
          )
        }
        source.addFeature(feature)
      })
      const vectorLayer = new VectorLayer({
        source: source,
        visible: options.visible
      })
      vectorLayer.set('id', id) // 给图层赋予id
      map.addLayer(vectorLayer)
      return vectorLayer
    } else {
      console.log('传入数据错误!')
    }
  }

  /**
   * 创建线类型图层
   * @param id string 图层id
   * @param dataArr Array<any> 对象数组，包含点位及相关信息
   * @param {LineLayerOptions}options  配置项
   * @returns VectorLayer | undefined
   */
  createLineLayer(id: string, dataArr: Array<any>, options?: LineLayerOptions): VectorLayer | undefined {
    const map = this.map
    if (!map) return
    options = options || {}
    if (options.visible === undefined) {
      options.visible = true
    }
    let style = new Style({
      stroke: new Stroke({
        color: '#f00',
        width: 2
      })
    })
    style = options.style || style
    if (dataArr && dataArr.length > 0) {
      const source = new VectorSource()
      dataArr.forEach((res) => {
        const paths: Array<Array<number>> = []
        res.paths.forEach((_res: Array<number>) => {
          paths.push([Number(_res[0]), Number(_res[1])])
        })
        const feature = new Feature({
          geometry: new LineString(paths),
          attributes: res
        })
        if (res.style) {
          feature.setStyle(res.style)
        }
        source.addFeature(feature)
      })
      const vectorLayer = new VectorLayer({
        source: source,
        visible: options.visible
      })
      vectorLayer.setStyle(style)
      vectorLayer.set('id', id) // 给图层赋予id
      map.addLayer(vectorLayer)
      return vectorLayer
    } else {
      console.log('传入数据错误!')
    }
  }
  /**
   * 创建多边形类型图层
   * @param id string 图层id
   * @param dataArr Array<any> 对象数组，包含点位及相关信息
   * @param {PolygonLayerOptions}options  配置项
   * @returns VectorLayer | undefined
   */
  createPolygonLayer(id: string, dataArr: Array<any>, options?: PolygonLayerOptions): VectorLayer | undefined {
    const map = this.map
    if (!map) return
    options = options || {}
    if (options.visible === undefined) {
      options.visible = true
    }
    let style = new Style({
      stroke: new Stroke({
        color: '#f00',
        width: 2
      }),
      fill: new Fill({
        color: '#ffb100d9'
      })
    })
    style = options.style || style
    if (dataArr && dataArr.length > 0) {
      const source = new VectorSource()
      dataArr.forEach((res) => {
        let geometry
        if (res.multiRings && res.multiRings.length > 0) {
          geometry = new MultiPolygon([res.multiRings])
        } else {
          const rings: Array<Array<number>> = []
          res.rings.forEach((_res: Array<number>) => {
            rings.push([Number(_res[0]), Number(_res[1])])
          })
          geometry = new Polygon([rings])
        }
        const feature = new Feature({
          geometry: geometry,
          attributes: res
        })
        if (res.style) {
          feature.setStyle(res.style)
        }
        source.addFeature(feature)
      })
      const vectorLayer = new VectorLayer({
        source: source,
        visible: options.visible
      })
      vectorLayer.setStyle(style)
      vectorLayer.set('id', id) // 给图层赋予id
      map.addLayer(vectorLayer)
      return vectorLayer
    } else {
      console.log('传入数据错误!')
    }
  }
  /**
   * 创建geojson图层
   * @param id string 图层id
   * @param data ArrayBuffer | Document | Element | Object | string，geojson数据源
   * @param {GeojsonLayerOptions}options  配置项
   * @returns VectorLayer | undefined
   */
  // 创建geojson图层 id:string 图层的id data:Array geojson对象，包含点位及相关信息 options:Obj 相关配置项（visible:boolean 图层是否显示）
  createGeojsonLayer(
    id: string,
    data: ArrayBuffer | Document | Element | Object | string,
    options?: GeojsonLayerOptions
  ): VectorLayer | undefined {
    const map = this.map
    if (!map) return
    options = options || {}
    if (options.visible === undefined) {
      options.visible = true
    }
    let style = new Style({
      stroke: new Stroke({
        color: '#f00',
        width: 2
      }),
      fill: new Fill({
        color: '#ffb100d9'
      })
    })
    style = options.style || style
    if (data) {
      const source = new VectorSource({
        features: new GeoJSON().readFeatures(data, {
          dataProjection: 'EPSG:4326'
        })
      })
      const vectorLayer = new VectorLayer({
        source: source,
        style: style,
        visible: options.visible
      })
      vectorLayer.set('id', id) // 给图层赋予id
      map.addLayer(vectorLayer)
      return vectorLayer
    } else {
      console.log('传入数据错误!')
    }
  }

  /**
   * 创建热力图层
   * @param id string 图层id
   * @param dataArr Array<any> 对象数组，包含点位及相关信息
   * @param {HeatMapLayerOptions}options  配置项
   * @returns Heatmap | undefined
   */
  createHeatMapLayer(id: string, dataArr: Array<any>, options?: HeatMapLayerOptions): Heatmap | undefined {
    const map = this.map
    if (!map) return
    options = options || {}
    if (options.visible === undefined) {
      options.visible = true
    }
    options.cntStr = options.cntStr || 'cnt'
    if (dataArr && dataArr.length > 0) {
      const source = new VectorSource()
      const cntArr: Array<number> = []
      dataArr.forEach((res) => {
        if (!res[options!.cntStr as string]) {
          res[options!.cntStr as string] = 1
        } else {
          res[options!.cntStr as string] = Number(res[options!.cntStr as string])
        }
        cntArr.push(res[options!.cntStr as string])
      })
      const maxCnt = Math.max.apply(null, cntArr)
      const minCnt = Math.min.apply(null, cntArr)
      dataArr.forEach((res) => {
        if (maxCnt === minCnt) {
          res.weight = 1 // weight为0到1之间
        } else {
          res.weight = (res[options!.cntStr as string] - minCnt) / (maxCnt - minCnt) || 0.1
        }
        const x = Number(res.longitude) || Number(res.x)
        const y = Number(res.latitude) || Number(res.y)
        let feature = new Feature({
          geometry: new Point([x, y]),
          weight: res.weight,
          attributes: res
        })
        source.addFeature(feature)
      })
      const heatMapLayer = new Heatmap({
        source: source,
        radius: options.radius || 8, // 热点半径
        blur: options.blur || 15, // 模糊尺寸
        weight: (feature) => {
          return feature.get('weight')
        },
        visible: options.visible
      })
      heatMapLayer.set('id', id) // 给图层赋予id
      map.addLayer(heatMapLayer)
      return heatMapLayer
    }
  }

  /**
   * 加载XYZ图层
   * @param {XYZLayerOptions}options  配置项
   * @returns TileLayer
   */
  loadXYZLayer(options: XYZLayerOptions): TileLayer<XYZ> {
    if (options.visible === undefined) {
      options.visible = true
    }
    const xyzLayer = new TileLayer({
      source: new XYZ({
        url: options.url,
        projection: options.projection, // 坐标系
        tileGrid: options.tileGrid, // 网格信息
        crossOrigin: options.crossOrigin || null // 跨域设置
      }),
      visible: options.visible
    })
    xyzLayer.set('id', options.id)
    return xyzLayer
  }

  /**
   * 加载矢量切片图层
   * @param {VectorTileLayerOptions}options  配置项
   * @returns VectorTileLayer
   */
  loadVectorTileLayer(options: VectorTileLayerOptions): VectorTileLayer {
    const vectorTileLayer = new VectorTileLayer({
      source: new VectorTile({
        format: new MVT(),
        url: options.url,
        tileGrid: new WMTSTileGrid({
          origin: (options.projection && olExtent.getTopLeft(options.projection.getExtent())) || undefined,
          resolutions: options.resolutions,
          matrixIds: options.matrixIds,
          tileSize: options.tileSize || 256
        }),
        projection: options.projection // 投影坐标系
      })
    })
    vectorTileLayer.set('id', options.id)
    return vectorTileLayer
  }

  /**
   * 加载WMTS图层
   * @param {WMTSLayerOptions}options  配置项
   * @returns TileLayer
   */
  loadWMTSLayer(options: WMTSLayerOptions): TileLayer {
    const tilelayer = new TileLayer({
      source: new WMTS({
        url: options.url, // 服务url
        layer: options.layer || '', // 需要加载的图层id
        version: options.version || '1.0.0', // WMTS版本
        style: options.style || '', // Style name as advertised in the WMTS capabilities.
        matrixSet: options.matrixSet, // 投影坐标系矩阵集，一定要和WMTS capabilities文档中一致，否则会加载失败
        format: options.format || 'image/png', // 图像格式,仅在请求编码为“KVP”时使用
        // 平铺方案
        tileGrid: new WMTSTileGrid({
          origin: (options.projection && olExtent.getTopLeft(options.projection.getExtent())) || undefined,
          resolutions: options.resolutions,
          matrixIds: options.matrixIds
        }),
        projection: options.projection, // 投影坐标系
        crossOrigin: options.crossOrigin || null // 跨域设置
      })
    })
    tilelayer.set('id', options.id)
    return tilelayer
  }

  /**
   * 自定义4490坐标系并注册 (此时的分辨率及层级矩阵为空，需要getResolutionsAndMids方法按实际需求设置)
   * @returns Projection
   */
  defineProjection_4490(): Projection {
    // 自定义坐标系
    const projection_4490 = new Projection({
      code: 'EPSG:4490',
      units: 'degrees',
      axisOrientation: 'enu'
    })
    projection_4490.setExtent([-180, -90, 180, 90])
    projection_4490.setWorldExtent([-180, -90, 180, 90])
    addProjection(projection_4490)
    return projection_4490
  }

  /**
   * 获取分辨率和层级矩阵
   * @param projection Projection 参考坐标系
   * @param defineSize number 切片分辨率 (默认256 一般为256*256或512*512)
   * @param minZoom number 最小层级
   * @param maxZoom number 最大层级
   * @returns ResolutionsAndMids
   */
  getResolutionsAndMids(
    projection: Projection,
    defineSize?: number,
    minZoom?: number,
    maxZoom?: number
  ): ResolutionsAndMids {
    minZoom = minZoom || 0
    maxZoom = maxZoom || 20
    defineSize = defineSize || 256
    const projection4326 = get('EPSG:4326')
    const projections = projection || projection4326
    const projectionExtent = projections.getExtent()
    const size = olExtent.getWidth(projectionExtent) / defineSize // size就是一个像素代表的经纬度
    const matrixIds: Array<any> = []
    const resolutions: Array<any> = []
    for (let z = minZoom; z < maxZoom; ++z) {
      resolutions[z] = size / Math.pow(2, z)
      matrixIds[z] = z.toString()
    }
    return { resolutions: resolutions, matrixIds: matrixIds }
  }

  /**
   * 定位到指定图层
   * @param id string 图层id
   * @param {GoToLayerOptions}options  配置项
   */
  goToLayer(id: string, options?: GoToLayerOptions) {
    options = options || {}
    if (options.duration === undefined) {
      options.duration = 200
    }
    const layer = this.getLayer(id) as VectorLayer | VectorImage | Heatmap | undefined
    if (!layer) {
      console.log('图层不存在!')
      return
    }
    let _extent: Array<number> = []
    // 判断图层是否为聚合图层
    const ifClusterLayer = layer && layer.getSource() instanceof Cluster
    if (ifClusterLayer) {
      const source = layer.getSource() as Cluster
      const _source = source.getSource()
      const clusterExtent = _source && _source.getExtent()
      const width = clusterExtent && olExtent.getWidth(clusterExtent)
      const height = clusterExtent && olExtent.getHeight(clusterExtent)
      const extent = clusterExtent
      if (extent && width && height) {
        _extent = [extent[0] - width * 0.1, extent[1] - height * 0.1, extent[2] + width * 0.1, extent[3] + height * 0.1]
      }
    } else {
      if (layer) {
        const source = layer.getSource()
        if (source) {
          if (source.getExtent) {
            const width = olExtent.getWidth(source.getExtent())
            const height = olExtent.getHeight(source.getExtent())
            const extent = source.getExtent()
            _extent = [
              extent[0] - width * 0.1,
              extent[1] - height * 0.1,
              extent[2] + width * 0.1,
              extent[3] + height * 0.1
            ]
          } else {
            const allFeatures = source.getFeaturesInExtent([-180, -90, 180, 90])
            let coordinates: Array<Array<number>> = []
            allFeatures.forEach((item) => {
              const itemExtent = item.getExtent()
              coordinates.push([itemExtent[0], itemExtent[1]])
              coordinates.push([itemExtent[2], itemExtent[3]])
            })
            _extent = olExtent.boundingExtent(coordinates)
          }
        }
      }
    }
    if (_extent && _extent.length > 0) {
      this.map &&
        this.map.getView().fit(_extent, {
          duration: options.duration,
          callback: options.callback
        })
    }
  }

  /**
   * 定位到指定点
   * @param {CenterAndZoomOptions}options  配置项
   */
  setCenterAndZoom(options: CenterAndZoomOptions) {
    if (options.duration === undefined) {
      options.duration = 500
    }
    this.map &&
      this.map.getView().animate({
        center: options.center,
        zoom: options.zoom,
        duration: options.duration,
        easing: (percent: number): number => {
          if (percent === 1) {
            // 定位过程的百分比
            options.callback && options.callback()
          }
          return percent
        }
      })
  }

  /**
   * 定位到指定要素
   * @param feature Feature 要素对象
   * @param {goToFeatureOptions}options  配置项
   */
  goToFeature(feature: Feature, options?: goToFeatureOptions) {
    options = options || {}
    if (options.duration === undefined) {
      options.duration = 500
    }
    const geometry = feature.getGeometry()
    const extent = geometry && geometry.getExtent()
    if (extent) {
      const _extent = olExtent.buffer(extent, 0.3) // 计算缓冲区
      this.map &&
        this.map.getView().fit(_extent, {
          duration: options.duration,
          callback: options.callback
        })
    }
  }

  /**
   * 创建飞线图
   * @param dataArr Array<any> 数据数组
   * @param {FlyLineOptions}options  配置项
   */
  createFlyLine(dataArr: Array<any>, options: FlyLineOptions) {
    if (!this.map) {
      return
    }
    const { parentDom, callback } = options
    if (dataArr && dataArr.length > 0) {
      var series: Array<any> = []
      var effectSymbol // 特效图形样式
      var effectSize // 特效图形大小
      var lineWith // 线的宽度
      // effectSymbol =   'image://' + pathSvg 加载图片的方式

      dataArr.forEach((item) => {
        if (item.type === 'airplane') {
          effectSize = 25
          effectSymbol =
            'path://M1705.06,1318.313v-89.254l-319.9-221.799l0.073-208.063c0.521-84.662-26.629-121.796-63.961-121.491c-37.332-0.305-64.482,36.829-63.961,121.491l0.073,208.063l-319.9,221.799v89.254l330.343-157.288l12.238,241.308l-134.449,92.931l0.531,42.034l175.125-42.917l175.125,42.917l0.531-42.034l-134.449-92.931l12.238-241.308L1705.06,1318.313z'
        } else if (item.type === 'car') {
          effectSize = 25
          effectSymbol =
            'path://M512 0c153.173333 0 277.333333 124.16 277.333333 277.333333v136.106667h42.666667a34.56 34.56 0 1 1 0 69.12h-42.666667V832a192 192 0 0 1-192 192h-170.666666a192 192 0 0 1-192-192V482.56H192a34.56 34.56 0 0 1-3.136-68.970667L192 413.44h42.666667V277.333333c0-153.173333 124.16-277.333333 277.333333-277.333333z m213.354667 775.552C663.402667 797.824 590.293333 810.666667 512 810.666667c-78.293333 0-151.381333-12.842667-213.333333-35.093334V832a128 128 0 0 0 128 128h170.666666a128 128 0 0 0 128-128l0.021334-56.448zM512 405.333333c-46.912 0-92.309333 5.077333-134.186667 14.634667C395.669333 465.045333 405.333333 519.210667 405.333333 576c0 56.810667-9.642667 110.933333-27.541333 156.032C419.690667 741.589333 465.066667 746.666667 512 746.666667c46.912 0 92.309333-5.077333 134.186667-14.634667C628.330667 686.954667 618.666667 632.789333 618.666667 576c0-53.013333 8.384-103.722667 24.064-146.901333l3.477333-9.130667-12.672-2.730667A612.992 612.992 0 0 0 512 405.333333z m213.333333 0c-25.92 38.912-42.666667 100.885333-42.666666 170.666667s16.746667 131.754667 42.666666 170.666667V405.333333z m-426.666666 0l0.021333 341.290667C324.586667 707.690667 341.333333 645.76 341.333333 576c0-69.781333-16.746667-131.733333-42.666666-170.666667zM512 64c-117.824 0-213.333333 95.509333-213.333333 213.333333v99.114667C360.618667 354.176 433.706667 341.333333 512 341.333333s151.381333 12.842667 213.333333 35.093334V277.333333c0-117.824-95.509333-213.333333-213.333333-213.333333z m95.445333 76.608l2.986667 0.96 40.106667 14.592a34.56 34.56 0 0 1-20.650667 65.898667l-2.986667-0.96-40.106666-14.592a34.56 34.56 0 0 1 20.650666-65.898667z m-149.589333 21.610667a34.56 34.56 0 0 1-17.749333 43.093333l-2.901334 1.194667-40.106666 14.592a34.56 34.56 0 0 1-26.538667-63.744l2.901333-1.194667 40.106667-14.592a34.56 34.56 0 0 1 44.288 20.650667z'
        } else {
          effectSize = 15
          effectSymbol = 'triangle'
        }
        lineWith = item.flyWireWidth || 1
        series.push(
          {
            type: 'lines',
            coordinateSystem: 'openlayers',
            zlevel: 1,
            effect: {
              show: true,
              period: 6,
              trailLength: 0.7,
              color: '#fff',
              symbolSize: lineWith + 1
            },
            lineStyle: {
              normal: {
                color: item.color,
                width: 0,
                curveness: 0.2
              }
            },
            tooltip: {
              trigger: 'item',
              show: false
            },
            data: item.data
          },
          {
            type: 'lines',
            coordinateSystem: 'openlayers',
            zlevel: 2,
            symbol: ['none', 'arrow'],
            symbolSize: 10,
            effect: {
              show: true,
              period: 6,
              trailLength: 0,
              symbol: effectSymbol,
              symbolSize: effectSize
            },
            lineStyle: {
              normal: {
                color: item.color,
                width: lineWith,
                opacity: 0.6,
                curveness: 0.2
              }
            },
            tooltip: {
              trigger: 'item',
              formatter: (param: any) => {
                callback && callback(param)
                return `${param.data.fromName} > ${param.data.toName} ${param.data.value}`
              },
              show: true
            },
            data: item.data
          },
          {
            type: 'effectScatter',
            coordinateSystem: 'openlayers',
            zlevel: 2,
            rippleEffect: {
              brushType: 'stroke'
            },
            label: {
              normal: {
                show: true,
                position: 'bottom',
                formatter: '{b}'
              }
            },
            tooltip: {
              trigger: 'item',
              show: false
            },
            symbolSize: 10,
            itemStyle: {
              normal: {
                color: item.color
              }
            },
            data: item.data.map(function (dataItem: any) {
              return {
                name: dataItem.fromName,
                value: dataItem.coords[0]
              }
            })
          },
          {
            type: 'effectScatter',
            coordinateSystem: 'openlayers',
            zlevel: 2,
            rippleEffect: {
              brushType: 'stroke'
            },
            label: {
              normal: {
                show: true,
                position: 'bottom',
                formatter: '{b}'
              }
            },
            tooltip: {
              trigger: 'item',
              show: false
            },
            symbolSize: 10,
            itemStyle: {
              normal: {
                color: item.color
              }
            },
            data: item.data.map(function (dataItem: any) {
              return {
                name: dataItem.toName,
                value: dataItem.coords[1]
              }
            })
          }
        )
      })
      var qianxiChart = new FlyLine(this.map!, { parentDom })
      var option = {
        tooltip: {
          trigger: 'item',
          show: true
        },
        series: series
      }
      qianxiChart.setChartOption(option)
      return qianxiChart
    }
  }

  /**
   * 检测经纬度是否合法，剔除无效数据
   * @param dataArr Array<any> 数据数组
   * @param {checkLongitudeAndLatitudeOptions} options  配置项
   * @returns Array<any> 有效数据
   */
  checkLongitudeAndLatitude(dataArr: Array<any>, options?: checkLongitudeAndLatitudeOptions): Array<any> {
    const l: Array<any> = []
    dataArr.forEach((item) => {
      item.x = Number(
        (options && options.longitudeStr && item[options.longitudeStr]) ||
          item.x ||
          item.longitude ||
          item.lng ||
          item.lon ||
          0
      )
      item.y = Number(
        (options && options.latitudeStr && item[options.latitudeStr]) || item.y || item.latitude || item.lat || 0
      )
      l.push(item)
    })
    return l
  }

  /**
   * 限制地图可拖动范围
   * @param {olExtent.Extent}maxExtent 最大范围
   * @param extentProjectionCode string 最大范围的坐标系
   * @returns 
   */
  setMapExtent(maxExtent: olExtent.Extent, extentProjectionCode?:string){
    if(!this.map){
      console.log('map 不存在！')
      return
    }
    const view = this.map.getView()
    if(!view) {
      console.log('view 不存在！')
      return
    }
    const projectionCode = this.map.getView().getProjection().getCode()
    if(!projectionCode){
      console.log('projectionCode 不存在！')
      return
    }
    if(!maxExtent){
      console.log('maxExtent 不存在！')
      return
    }
    const extent = transformExtent(maxExtent, extentProjectionCode || 'EPSG:4326', projectionCode)

    view.applyOptions_({
      extent,
      constrainOnlyCenter: false, // 如果为true，则区段约束将仅适用于视图中心，而不是整个区段。
      multiWorld: false // 如果为false，则视图受到约束，因此只有一个世界可见，并且您无法平移边缘。如果为true，地图可能会以低缩放级别显示多个世界。仅在投影是全局的情况下使用。请注意，如果还提供了范围，则优先考虑。
    })
  }
}
export { OlUtil }
